EtherCAT主站 您所在的位置:网站首页 simpletest 2mb报错 EtherCAT主站

EtherCAT主站

#EtherCAT主站| 来源: 网络整理| 查看: 265

目录

1、SOEM 库架构分析

2、硬件抽象层

3、中间层

4、SOEM1.3.1源代码目录介绍:

1、SOEM 库架构分析         SOEM 是 Simple Open EtherCAT Master Library 的缩写,是瑞典 rt-lab 提供 的一个开源 EtherCAT 主站协议库 。 SOEM 库使用 C 语言编写,可以在 windows 以及 Linux 平台上运行,并也可以方便地移植到嵌入式平台上。         SOEM 支持 CoE , SoE , FoE 以及分布式时钟。 SOEM 直接将 EtherCAT 帧 通过 MAC 发送和接收,因此它不支持 EoE 。 SOEM 库由若干模块组成,最底层提供硬件和操作系统抽象层,从而可以 方便地将 SOEM 库移植到不同的系统平台上。 SOEM 库架构如图 所示:

         SOEM 库采用分层设计,并且提供了一个抽象层,将 SOEM 协议栈与具体 操作系统和硬件分开,使得 SOEM 在理论上可以移植到任意操作系统和硬件平 台之上。

        抽象层由 OSAL 和 OSHW 两个模块组成,OSAL 是操作系统抽象层,OSHW 是硬件抽象层,移植的主要内容就是对 OSAL 和 OSHW 具体 API 实现,在新的 操作系统和硬件平台上的重写。注意,SOEM 可以不需要 OS 操作系统,直接在时间和定时器相关的 API 是必须实现的。

2、硬件抽象层         硬件抽象层即OSHW 模块,它为上层提供网络服务,是最重要的实现。 SOEM 移植的核心工作就是对此模块的移植,后文会详细介绍具体的实现。         OSHW 模块由 oshw.h/oshw.c 和 nicdrv.h/nicdrv.c 四个文件组成。 oshw.h/.c 主要实现网络小端和本地大小端的转换,nicdrv.h/.c 是网络驱动,主要实现 EtherCAT 帧的发送和接收, SOEM 的移植主要是对网络驱动的实现。 oshw.h/.c 主要提供字节顺序转换的服务。数据在网络上传输时,总是先传 输高位比特,然后传低位比特。所以,网络采用的是大端方式。而具体一个硬 件平台,可能采用大端也可能是小端。          不同平台上 nicdrv 模块的逻辑结构基本相同,需要实现的是三个底层 API : ( 1 )网口的初始化 (2) MAC 层帧发送 (3) MAC 层帧接收 3、中间层         抽象层之上的模块(除了应用层)属于 SOEM 的中间层,是 EtherCAT 协议 栈的具体实现,包含 BASE 模块、 MAIN 模块、 CONFIG 模块、 CONFIGDC 模 块、COE 等。各层的功能如下: 层功能

ethercatbase

将工业应用数据组装成 EtherCAT 帧, 顺序寻址、广播方式、配置地址、逻辑地址方式对从站读写

ethercatmain

读写从站 EEPROM , 提供邮箱模式的非过程数据读写和三缓冲模式的过程数据 PDO 读写

ethercatconfig

初始化从站控制器的寄存器,配置从站 FMMU

ethercatdc

提供分布式时钟,实现主从站之间时钟同步

ethercatsoe

CANOpen Over EtherCAT

ethercatfoe

File Over EtherCAT

ethercatsoe

Sercos Over EtherCAT

4、SOEM1.3.1源代码目录介绍:

doc:帮助文档、osal:操作系统抽象层,主要是用于符合OSADL和实时进程创建。也就是说:发送EtherCAT数据包不能抖动太大,如果直接使用linux提供的原生线程,可能实时性无法满足。需要对Linux内核打上实时补丁,我们采用PREEMPT_RToshw:硬件抽象层,网卡的接口封装,不同操作系统对网卡操作不一样,linux下我们要求网卡支持混杂模式。 soem:EtherCAT主站的核心代码。包括COE,FOE等等。

5、代码讲解

main函数先开线程:

int main(int argc, char *argv[]) { printf("SOEM (Simple Open EtherCAT Master)\nSimple test\n"); if (argc > 1) { /* create thread to handle slave error handling in OP */ // pthread_create( &thread1, NULL, (void *) &ecatcheck, (void*) &ctime); osal_thread_create(&thread1, 128000, &ecatcheck, (void*) &ctime); /* start cyclic part */ simpletest(argv[1]); } else { printf("Usage: simple_test ifname1\nifname = eth0 for example\n"); } printf("End program\n"); return (0); }

 开了线程之后,调用simpletest函数,并把网卡名作为输入参数。

进入simpletest,第一步是调用ec_init函数:

int ec_init(const char * ifname) { return ecx_init(&ecx_context, ifname); }

注意一下ecx_context,这个结构体定义如下:

struct ecx_context { /** port reference, may include red_port */ ecx_portt *port; /** slavelist reference */ ec_slavet *slavelist; /** number of slaves found in configuration */ int *slavecount; /** maximum number of slaves allowed in slavelist */ int maxslave; /** grouplist reference */ ec_groupt *grouplist; /** maximum number of groups allowed in grouplist */ int maxgroup; /** internal, reference to eeprom cache buffer */ uint8 *esibuf; /** internal, reference to eeprom cache map */ uint32 *esimap; /** internal, current slave for eeprom cache */ uint16 esislave; /** internal, reference to error list */ ec_eringt *elist; /** internal, reference to processdata stack buffer info */ ec_idxstackT *idxstack; /** reference to ecaterror state */ boolean *ecaterror; /** internal, position of DC datagram in process data packet */ uint16 DCtO; /** internal, length of DC datagram */ uint16 DCl; /** reference to last DC time from slaves */ int64 *DCtime; /** internal, SM buffer */ ec_SMcommtypet *SMcommtype; /** internal, PDO assign list */ ec_PDOassignt *PDOassign; /** internal, PDO description list */ ec_PDOdesct *PDOdesc; /** internal, SM list from eeprom */ ec_eepromSMt *eepSM; /** internal, FMMU list from eeprom */ ec_eepromFMMUt *eepFMMU; /** registered FoE hook */ int (*FOEhook)(uint16 slave, int packetnumber, int datasize); /** registered EoE hook */ int (*EOEhook)(ecx_contextt * context, uint16 slave, void * eoembx); /** flag to control legacy automatic state change or manual state change */ int manualstatechange; };

于是,变量ecx_context又把总线运行的一系列信息放在了这里:

ecx_contextt ecx_context = { &ecx_port, // .port = &ec_slave[0], // .slavelist = &ec_slavecount, // .slavecount = EC_MAXSLAVE, // .maxslave = &ec_group[0], // .grouplist = EC_MAXGROUP, // .maxgroup = &ec_esibuf[0], // .esibuf = &ec_esimap[0], // .esimap = 0, // .esislave = &ec_elist, // .elist = &ec_idxstack, // .idxstack = &EcatError, // .ecaterror = 0, // .DCtO = 0, // .DCl = &ec_DCtime, // .DCtime = &ec_SMcommtype[0], // .SMcommtype = &ec_PDOassign[0], // .PDOassign = &ec_PDOdesc[0], // .PDOdesc = &ec_SM, // .eepSM = &ec_FMMU, // .eepFMMU = NULL, // .FOEhook() NULL, // .EOEhook() 0 // .manualstatechange };

再然后,ecx_init调用了ecx_setupnic:套娃套娃

int ecx_init(ecx_contextt *context, const char * ifname) { return ecx_setupnic(context->port, ifname, FALSE); }

最终是这个函数:

int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) { int i; char ifn[IF_NAME_SIZE]; int unit_no = -1; ETHERCAT_PKT_DEV * pPktDev; /* Get systick info, sysClkRateGet return ticks per second */ usec_per_tick = USECS_PER_SEC / sysClkRateGet(); /* Don't allow 0 since it is used in DIV */ if(usec_per_tick == 0) usec_per_tick = 1; /* Make reference to packet device struct, keep track if the packet * device is the redundant or not. */ if (secondary) { pPktDev = &(port->redport->pktDev); pPktDev->redundant = 1; } else { pPktDev = &(port->pktDev); pPktDev->redundant = 0; } /* Clear frame counters*/ pPktDev->tx_count = 0; pPktDev->rx_count = 0; pPktDev->overrun_count = 0; /* Create multi-thread support semaphores */ port->sem_get_index = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE); /* Get the dev name and unit from ifname * We assume form gei1, fei0... */ memset(ifn,0x0,sizeof(ifn)); for(i=0; i < strlen(ifname);i++) { if(isdigit(ifname[i])) { strncpy(ifn, ifname, i); unit_no = atoi(&ifname[i]); break; } } /* Detach IP stack */ //ipDetach(pktDev.unit,pktDev.name); pPktDev->port = port; /* Bind to mux driver for given interface, include ethercat driver pointer * as user reference */ /* Bind to mux */ pPktDev->pCookie = muxBind(ifn, unit_no, mux_rx_callback, NULL, NULL, NULL, MUX_PROTO_SNARF, "ECAT SNARF", pPktDev); if (pPktDev->pCookie == NULL) { /* fail */ NIC_LOGMSG("ecx_setupnic: muxBind init for gei: %d failed\n", unit_no, 2, 3, 4, 5, 6); goto exit; } /* Get reference tp END obje */ pPktDev->endObj = endFindByName(ifn, unit_no); if (port->pktDev.endObj == NULL) { /* fail */ NIC_LOGMSG("error_hook: endFindByName failed, device gei: %d not found\n", unit_no, 2, 3, 4, 5, 6); goto exit; } if (secondary) { /* secondary port struct available? */ if (port->redport) { port->redstate = ECT_RED_DOUBLE; port->redport->stack.txbuf = &(port->txbuf); port->redport->stack.txbuflength = &(port->txbuflength); port->redport->stack.rxbuf = &(port->redport->rxbuf); port->redport->stack.rxbufstat = &(port->redport->rxbufstat); port->redport->stack.rxsa = &(port->redport->rxsa); /* Create mailboxes for each potential EtherCAT frame index */ for (i = 0; i < EC_MAXBUF; i++) { port->redport->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO); if (port->redport->msgQId[i] == MSG_Q_ID_NULL) { NIC_LOGMSG("ecx_setupnic: Failed to create redundant MsgQ[%d]", i, 2, 3, 4, 5, 6); goto exit; } } ecx_clear_rxbufstat(&(port->redport->rxbufstat[0])); } else { /* fail */ NIC_LOGMSG("ecx_setupnic: Redundant port not allocated", unit_no, 2, 3, 4, 5, 6); goto exit; } } else { port->lastidx = 0; port->redstate = ECT_RED_NONE; port->stack.txbuf = &(port->txbuf); port->stack.txbuflength = &(port->txbuflength); port->stack.rxbuf = &(port->rxbuf); port->stack.rxbufstat = &(port->rxbufstat); port->stack.rxsa = &(port->rxsa); /* Create mailboxes for each potential EtherCAT frame index */ for (i = 0; i < EC_MAXBUF; i++) { port->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO); if (port->msgQId[i] == MSG_Q_ID_NULL) { NIC_LOGMSG("ecx_setupnic: Failed to create MsgQ[%d]", i, 2, 3, 4, 5, 6); goto exit; } } ecx_clear_rxbufstat(&(port->rxbufstat[0])); } /* setup ethernet headers in tx buffers so we don't have to repeat it */ for (i = 0; i < EC_MAXBUF; i++) { ec_setupheader(&(port->txbuf[i])); port->rxbufstat[i] = EC_BUF_EMPTY; } ec_setupheader(&(port->txbuf2)); return 1; exit: return 0; }

简单来说,就是分配收发缓冲区地址,打开硬件,再把数据包头写到每一个发送缓冲区首部,免得后续每次都写。另外初始化了一些保护关键代码段的互斥锁。如果是裸跑的话,在保护关键代码的时候可能要考虑用开关中断来实现了。再一个,可以看到,这里实际上是可以打开第二个网口的。两个网口,一个作为输出,一个作为输入。这个按实际情况来做吧,目前见到的应用,多数是只用一个网口的。

这里的重点在于这个port。可以看到,实际上这里是用了在ethercatmain.c文件中定义的全局变量:

ecx_portt               ecx_port;

还记得之前说的那个ecx_context吗?对,这个ecx_port就是ecx_context里面的。

在nicdrv.h文件中,这个exc_portt定义如下:

typedef struct ecx_port { /** Stack reference */ ec_stackT stack; /** Packet device instance */ ETHERCAT_PKT_DEV pktDev; /** rx buffers */ ec_bufT rxbuf[EC_MAXBUF]; /** rx buffer status */ int rxbufstat[EC_MAXBUF]; /** rx MAC source address */ int rxsa[EC_MAXBUF]; /** transmit buffers */ ec_bufT txbuf[EC_MAXBUF]; /** transmit buffer lengths */ int txbuflength[EC_MAXBUF]; /** temporary tx buffer */ ec_bufT txbuf2; /** temporary tx buffer length */ int txbuflength2; /** last used frame index */ int lastidx; /** current redundancy state */ int redstate; /** pointer to redundancy port and buffers */ ecx_redportt *redport; /** Semaphore to protect single resources */ SEM_ID sem_get_index; /** MSG Q for receive callbacks to post into */ MSG_Q_ID msgQId[EC_MAXBUF]; } ecx_portt;

这里EC_MAXBUF是ethercattype.h文件当中的宏定义:

#define EC_MAXBUF          16

该文件在SOEM文件夹当中。还有ec_bufT的定义

#define EC_MAXECATFRAME    1518

#define EC_BUFSIZE         EC_MAXECATFRAME

typedef uint8 ec_bufT[EC_BUFSIZE];

这个1518数字看着眼熟。看看EtherCAT数据帧格式就一目了然了:

顺便也说一下这个ecx_redportt结构体:

/** pointer structure to buffers for redundant port */ typedef struct ecx_redport { /** Stack reference */ ec_stackT stack; /** Packet device instance */ ETHERCAT_PKT_DEV pktDev; /** rx buffers */ ec_bufT rxbuf[EC_MAXBUF]; /** rx buffer status */ int rxbufstat[EC_MAXBUF]; /** rx MAC source address */ int rxsa[EC_MAXBUF]; /** MSG Q for receive callbacks to post into */ MSG_Q_ID msgQId[EC_MAXBUF]; } ecx_redportt;

    依然是在nicdrv.h当中定义。这就是一个只有接收功能的ecx_portt的阉割版本,没有发送缓冲区及其状态标志,因为收发过程中的关键代码保护互斥量已经有了,所以这里连互斥量都省了。细心的你可能在ecx_portt当中发现一个问题:



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有